// parser 相关

// 定义状态机的状态
const State = {
    initial: 1, // 初始状态
    tagOpen: 2, // 标签开始状态
    tagName: 3, // 标签名称状态
    text: 4, // 文本状态
    tagEnd: 5, // 结束标签状态
    tagEndName: 6, // 结束标签名称状态
}

function isAlpha (char) {
    return (char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z');
}

// 接收模板字符串作为参数，并将模板切割为 Token 返回

function tokenize (str) {
    // 状态机当前的状态：初始状态
    let currentState = State.initial;

    // 用于缓存 字符
    const chars = []

    // 用于缓存 token 类型和值 
    const tokens = []

    while (str) {
        const char = str[0];
        switch (currentState) {
            case State.initial:
                if (char === '<') {
                    currentState = State.tagOpen;
                    str = str.slice(1);
                } else if (isAlpha(char)) {
                    currentState = State.text;
                    chars.push(char);
                    str = str.slice(1);
                }
                break;

            case State.tagOpen:
                if (isAlpha(char)) {
                    currentState = State.tagName;
                    chars.push(char);
                    str = str.slice(1);
                } else if (char === '/') {
                    currentState = State.tagEnd;
                    str = str.slice(1);
                }
                break;

            case State.tagName:
                if (char === '>') {
                    currentState = State.initial;
                    // 创建标签Token 存入Tokens
                    tokens.push({
                        type: 'tag',
                        name: chars.join(''),
                    });
                    // 清空 chars
                    chars.length = 0;
                    str = str.slice(1);
                } else if (isAlpha(char)) {
                    chars.push(char);
                    str = str.slice(1);
                }
                break;

            case State.text:
                if (isAlpha(char)) {
                    // 遇到字母状态不改变，
                    chars.push(char);
                    str = str.slice(1);
                } else if (char === '<') {
                    currentState = State.tagOpen;

                    tokens.push({
                        type: 'text',
                        content: chars.join(''),
                    });
                    chars.length = 0;
                    str = str.slice(1);
                }
                break;

            case State.tagEnd:
                if (isAlpha(char)) {
                    currentState = State.tagEndName;
                    chars.push(char);
                    str = str.slice(1);
                }
                break;

            case State.tagEndName:
                if (isAlpha(char)) {
                    chars.push(char);
                    str = str.slice(1);
                } else if (char === '>') {
                    currentState = State.initial;
                    tokens.push({
                        type: 'tagEnd',
                        name: chars.join(''),
                    });
                    chars.length = 0;
                    str = str.slice(1);
                }
                break;
        }
    }
    return tokens;
}

const tokens = tokenize(`<div>hello</div>`);
console.log('%c [ tokens ]-116', 'font-size:13px; background:pink; color:#bf2c9f;', tokens)

const tokens1 = tokenize(`<div><p>Vue</p><p>Template</p></div>`)
console.log('[38;5;196m [ tokens1 ]-119 [0m', tokens1)


// Tokens 类型
const TAG_TYPE = {
    tag: 'tag',
    text: 'text',
    tagEnd: 'tagEnd'
}



// AST 抽象语法树转化
function parse (str) {
    // 对模板进行标记化，获取tokens
    const tokens = tokenize(str)

    // 创建根节点
    const Root = {
        type: 'Root',
        children: [],
    }

    // 创建elementStack栈 初始化 只有 根节点
    const elementStack = [Root]

    while (tokens.length) {
        // 获取栈顶元素 作为 父节点 parent 
        const parent = elementStack[elementStack.length - 1]

        const t = tokens[0]

        switch (t.type) {
            case TAG_TYPE.tag:
                const element = {
                    type: 'Element',
                    tag: t.name,
                    children: [],
                }
                // 添加到父节点的children中
                parent.children.push(element)
                // 压栈
                elementStack.push(element)
                break;

            case TAG_TYPE.text:
                const textNode = {
                    type: 'Text',
                    content: t.content,
                }
                parent.children.push(textNode)
                break;

            case TAG_TYPE.tagEnd:
                // 结束 则把 栈顶元素弹出
                elementStack.pop()
                break;
        }

        // 消费掉已经遍历过的token
        tokens.shift()

    }

    return Root
}

const templateStr = '<div><p>Vue</p><p>Template</p></div>'

const AST = parse(templateStr)
// console.log('[90m [ AST ]-189 [0m', JSON.stringify(AST, null, 2))




// dump 函数 用于打印AST

function dump (node, indent = 0) {
    // 节点类型
    const type = node.type

    const desc = type === 'Root' ? '' : (
        type === 'Element' ? node.tag : node.content
    )

    console.log('[38;2;0;255;0m [  ]-204 [0m', `${'-'.repeat(indent)}${type}:${desc}`)

    if (node.children) {
        node.children.forEach(child => {
            dump(child, indent + 2)
        })
    }
}


console.log('[30m [ AST ]-214 [0m',)
console.log('[30m [ AST ]-214 [0m', dump(AST))

console.log('',)
console.log('',)
console.log('',)
function traverseNode (ast) {
    // ast 本身就是 根节点
    const currentNode = ast

    if (currentNode.type === 'Element' && currentNode.tag === 'p') {
        currentNode.tag = 'h1'
    }


    // 递归处理每一个子节点
    const children = currentNode.children
    if (children) {
        children.forEach(child => {
            traverseNode(child)
        })
    }

}

function transform (ast) {
    traverseNode(ast)

    console.log('[48;2;0;0;255m [ transform ]-240 [0m',)
    console.log('[48;2;0;0;255m [ transform ]-240 [0m', dump(ast))
}

transform(JSON.parse(JSON.stringify(AST)))


console.log('',)
console.log('',)
console.log('',)
console.log('',)
// 解耦写法 START
function traverseNode1 (ast, context) {
    // ast 本身就是 根节点
    const currentNode = ast

    // transforms 是一个数组 里面装着各种转换的函数
    const transforms = context.nodeTransforms
    for (let i = 0; i < transforms.length; i++) {
        const transform = transforms[i]
        transform(currentNode, context)
    }



    // 递归处理每一个子节点
    const children = currentNode.children
    if (children) {
        children.forEach(child => {
            traverseNode1(child, context)
        })
    }
}

function transform1 (ast) {
    const context = {
        nodeTransforms: [
            transformElement,
            transformText
        ]
    }

    traverseNode1(ast, context)

    console.log('[38;5;196m [ transform 解耦写法 START]-269 [0m',)
    console.log('[38;5;196m [ transform 解耦写法 END]-269 [0m', dump(ast))
}

// 处理 p标签
function transformElement (node) {
    if (node.type === 'Element' && node.tag === 'p') {
        node.tag = 'h2'
    }
}

// 处理文本内容
function transformText (node) {
    if (node.type === 'Text') {
        node.content = node.content.repeat(2)
    }
}

transform1(JSON.parse(JSON.stringify(AST)))

// 解耦写法  END